# coding:utf-8
from datetime import datetime

from flask import current_app, jsonify, json, request, g, session

from ihome import redis_store, constants, db
from ihome.api_1_0 import api
from ihome.models import Area, House, Facility, HouseImage, User, Order
from ihome.utils.commons import login_required
from ihome.utils.image_storage import storage
from ihome.utils.response_code import RET


@api.route('/areas')
def get_area_info():
    """获取城区信息"""
    # 先尝试从redis中获取缓存数据
    try:
        areas_json = redis_store.get('area_info')
    except Exception as e:
        current_app.logger.error(e)
        areas_json = None
    if areas_json is None:
        # 查询数据库，获取城区信息
        try:
            areas_list = Area.query.all()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='查询城区信息异常')
        # 遍历列表，处理每一个对象，转换一下对象的属性，性名
        areas = []
        for area in areas_list:
            areas.append(area.to_dict())
        # 将数据转为json
        areas_json = json.dumps(areas)
        #  将数据在redis中保存一份缓存
        try:
            # mysql数据与redis的数据同步问题解决，对缓存数据设置有效期
            redis_store.setex('area_info', constants.AREA_INFO_REDIS_EXPIRES, areas_json)
        except Exception as e:
            current_app.logger.error(e)
    else:
        # 表示redis中有缓存，直接使用的是缓存
        current_app.logger.info('hit redis cache area info')
    # 从redis中去取的json数据或者从数据库中查询并转为json数据
    #  areas_json ='[{'aid':xx,'aname':xxx},{},{}]'
    return '{"errno":"0", "errmsg": "查询城区信息成功", "data":{"areas": %s}}' % areas_json, 200, {
        "Content-Type": "application/json"}


@api.route('/houses/info', methods=['POST'])
@login_required
def save_house_info():
    """保存房屋的基本信息"""
    house_data = request.get_json()
    if house_data is None:
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')
    title = house_data.get('title')
    price = house_data.get('price')
    area_id = house_data.get('area_id')
    address = house_data.get('address')
    room_count = house_data.get('room_count')
    acreage = house_data.get('acreage')
    unit = house_data.get('unit')
    capacity = house_data.get('capacity')
    beds = house_data.get('beds')
    deposit = house_data.get('deposit')
    min_days = house_data.get('min_days')
    max_days = house_data.get('max_days')
    if not all(
            [title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')
    # 判断单价和押金格式是否正确
    # 前端传送过来的金额参数是以元为单位，浮点数，数据库中保存的是以分为单位，整数
    try:
        price = int(float(price) * 100)
        deposit = int(float(deposit) * 100)
    except Exception as e:
        return jsonify(errno=RET.DATAEXIST, errmsg='参数有误')
    # 保存信息
    user_id = g.user_id
    house = House(
        user_id=user_id,
        area_id=area_id,
        title=title,
        price=price,
        address=address,
        room_count=room_count,
        acreage=acreage,
        unit=unit,
        capacity=capacity,
        beds=beds,
        deposit=deposit,
        min_days=min_days,
        max_days=max_days
    )
    # 处理房屋的设施信息
    facility_id_list = house_data.get('facility')
    if facility_id_list:
        # 表示用户勾选了房屋设施
        # 过滤用户传送的不合理的设施id
        try:
            facility_list = Facility.query.filter(Facility.id.in_(facility_id_list)).all()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='数据库异常')
        # 为房屋添加设施信息
        if facility_list:
            house.facilities = facility_list
    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存数据失败')
    return jsonify(errno=RET.OK, errmsg='保存成功', data={'house_id': house.id})


@api.route('/houses/image', methods=['POST'])
@login_required
def save_house_image():
    """保存房屋图片 """
    # 获取参数  房屋的图片 房屋编号
    house_id = request.form.get('house_id')
    image_file = request.files.get('house_image')
    # 校验参数
    if not all([house_id, image_file]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')
    # 判断房屋是否存在
    try:
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库异常')
    if house is None:
        return jsonify(errno=RET.NODATA, errmsg='房屋不存在')
    # 上传房屋图片到七牛中
    image_data = image_file.read()
    try:
        file_name = storage(image_data)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='保存房屋图片失败')
    # 保存图片信息到数据库中
    house_image = HouseImage(
        house_id=house_id,
        url=file_name
    )
    db.session.add(house_image)
    # 处理房屋基本信息中的主图片：
    if not house.index_image_url:
        house.index_image_url = file_name
        db.session.add(house)
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存图片信息失败')
    image_url = constants.QINIU_URL_DOMAIN + file_name
    return jsonify(errno=RET.OK, errmsg='保存图片成功', data={'image_url': image_url})


@api.route('/user/houses', methods=['GET'])
@login_required
def get_user_houses():
    """获取房东发布的房源信息条目"""
    user_id = g.user_id
    try:
        # houses = House.query.filter_by(user_id = user_id)
        user = User.query.get(user_id)
        houses = user.houses
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='获取数据失败')
    # 将查询到的房屋信息转为字典存放到列表中
    houses_list = []
    if houses:
        for house in houses:
            houses_list.append(house.to_basic_dict())
    return jsonify(errno=RET.OK, errmsg='OK', data={'houses': houses_list})


@api.route('/houses/index', methods=['GET'])
def get_house_index():
    """获取主页幻灯片展示的房屋基本信息"""
    # 从缓存中尝试获取数据
    try:
        ret = redis_store.get('home_page_data')
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    if ret:
        current_app.logger.info('hit house index info redis')
        # 因为redis中保存的是json字符串，所有直接进行字符串
        return '{"errno":0,"errmsg":"OK","data":%s}' % ret, 200, {'Content-Type': 'application/json'}
    else:
        try:
            # 查询数据库，返回房屋订单数目最多的5条数目,limit限制是返回几条
            houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='查询数据库失败')
        if not houses:
            return jsonify(errno=RET.NODATA, errmsg='查询无数据')
        houses_list = []
        for house in houses:
            # 如果房屋未设置主图片，则跳过
            if not house.index_image_url:
                continue
            houses_list.append(house.to_basic_dict())
        # 将数据转换为json，并保存到redis缓存
        json_houses = json.dumps(houses_list)
        try:
            redis_store.setex('home_page_data', constants.HOME_PAGE_DATA_REDIS_EXPIRES, json_houses)
        except Exception as e:
            current_app.logger.error(e)
        # 告诉响应报文，传的是json格式
        return '{"errno":0,"errmsg":"OK","data":%s}' % json_houses, 200, {'Content-Type': 'application/json'}


# 房屋详情页
# 首先需要房屋基本信息，标题，地址，等，需要的信息都转为字典to_full_dict()
@api.route('/houses/<int:house_id>', methods=['GET'])
def get_house_detail(house_id):
    """获取房屋详情页"""
    #  前端在房屋详情页面展示时，如果浏览页面的用户不是该房屋的房东，则展示预定按钮，否则不展示
    # 所以需要后端返回登录用户的user_id
    # 尝试获取用户登录的信息，若登录，则返回给前端登录用户的user_id,否则返回user_id = -1
    user_id = session.get('user_id', "-1")
    # 校验参数
    if not house_id:
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 先从redis缓存中获取信息
    try:
        ret = redis_store.get('house_info_%s' % house_id)
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    if ret:
        current_app.logger.info('hit house info redis')
        return '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}' % (user_id, ret), 200, {
            "Content-Type": "aplication/json"}
    # 查询数据库
    try:
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.NODATA, errmsg="房屋不存在")
    # 将房屋对象数据转换为字典
    try:
        house_data = house.to_full_dict()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg="数据库出错")
    # 存入到redis中
    json_house = json.dumps(house_data)
    try:
        redis_store.setex("house_info_%s" % house_id, constants.HOUSES_DETAIL_REDIS_EXPIRE_SECOND, json_house)
    except Exception as e:
        current_app.logger.error(e)
    resp = '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}' % (user_id, json_house), 200, {
        "Content-Type": "application/json"}
    return resp


# 最终路径 /api/v1.0/houses?sd=xxxx&ed=xxxx
@api.route('/houses', methods=['GET'])
def get_house_list():
    """获取房屋列表信息"""
    # 获取参数 , 起始时间  查询的终止时间
    start_date_str = request.args.get('sd', "")
    end_date_str = request.args.get('ed', "")
    #  区域id
    area_id = request.args.get('aid', "")
    # 排序关键字,没有提排序关键字，那么自己设置一个，默认的是新旧排序
    sort_key = request.args.get('sk', 'new')
    # 页数
    page = request.args.get('p', 1)
    # 校验参数  判断日期
    try:
        start_date = None
        if start_date_str:
            #  时间格式转换
            start_date = datetime.strptime(start_date_str, "%Y-%m-%d")
        end_date = None
        if end_date_str:
            end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
        if start_date and end_date:
            # assert关键字，单元测试的布尔断言方法 assertTrue 和 assertFalse
            assert start_date <= end_date
    except Exception as e:
        return jsonify(errno=RET.PARAMERR, errmsg='日期参数有误')
    # 判断页数
    try:
        page = int(page)
    except Exception:
        page = 1
    # 从redids缓存中获取数据
    try:
        redis_key = 'houses_%s_%s_%s_%s' % (start_date_str, end_date_str, area_id, sort_key)
        resp_json = redis_store.hget(redis_key, page)
    except Exception as e:
        current_app.logger.error(e)
        resp_json = None
    if resp_json:
        # 表示从缓存中拿到数据
        return resp_json, 200, {'Content-Type': 'application/json'}
    # 查询数据
    # 处理区域信息
    filter_params = []  # 过滤参数列表
    if area_id:
        # 如果House.area_id == area_id，并不是返回的是true和false
        filter_params.append(House.area_id == area_id)
    # 处理时间
    try:
        conflict_orders_li = []
        if start_date and end_date:
            # 从订单表中查询冲突的订单，进而获取冲突的房屋id
            # ,是并且的意思，start_data,end_data 用户预期的起始/结束时间 ，order.begin_data,ordre.end_data 订单的起始/终止时间 ，
            conflict_orders_li = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            # 从订单表中查询冲突的订单，进而获取冲突的房屋id
            conflict_orders_li = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            conflict_orders_li = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库异常')
    if conflict_orders_li:
        #  遍历列表，取出一个一个订单，在订单房屋id正好是冲突房屋id
        conflict_house_id_li = [order.house_id for order in conflict_orders_li]
        # 添加条件 ，查询不冲突房屋
        filter_params.append(House.id.notin_(conflict_house_id_li))
    # 排序
    if sort_key == 'booking':
        # 按入住最多的排序 降序排序
        house_query = House.query.filter(*filter_params).order_by(House.order_count.desc())
    elif sort_key == 'price-inc':
        # 价格 低-高 排序
        house_query = House.query.filter(*filter_params).order_by(House.price.asc())
    elif sort_key == 'price-des':
        # 价格 高-低 排序
        house_query = House.query.filter(*filter_params).order_by(House.price.desc())
    else:
        house_query = House.query.filter(*filter_params).order_by(House.create_time.desc())
    # 分页   sqlalchemy分页
    try:
        # page,页数，每页数量，错误输出
        house_page = house_query.paginate(page, constants.HOUSE_LIST_PAGE_CAPACITY, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库异常')
    house_li = house_page.items  # 当前页中的数据结果
    total_page = house_page.pages  # 总页数
    houses = []
    for house in house_li:
        houses.append(house.to_basic_dict())
    # 响应报文转为字典
    resp = dict(errno=RET.OK, errmsg='查询成功', data={'houses': houses, 'total_page': total_page, 'current_page': page})
    # json.dumps() 将字典转换成字符串
    resp_json = json.dumps(resp)
    # 将结果缓存到redis中
    if page <= total_page:
        # 用redis的哈希类型保存分布数据,
        redis_key = "houses_%s_%s_%s_%s" % (start_date_str, end_date_str, area_id, sort_key)
        try:
            # 使用redis中的事务
            pipeline = redis_store.pipeline()
            # 开启事务
            pipeline.multi()
            # 开启事务后，就不再通过redis_key去执行，而是通过pipeline
            pipeline.hset(redis_key, page, resp_json)
            pipeline.expire(redis_key, constants.HOUSE_LIST_PAGE_REDIS_EXPIRES)
            # 执行事务
            pipeline.execute()
        except Exception as e:
            current_app.logger.error(e)
    return resp_json, 200, {'Content-Type': 'application/json'}
    # # house.id在某个范围内
    # House.id.in_()
    # # 当参数个数不确定的时候，记得使用*filter_params
    # House.query.filter(*filter_params).order_by()
